#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>
/* combine 算法 */

typedef struct _cacccsynnode
{
	APDSFUTURE_DECL
	union{
		struct{
			hword_t	cid;
			apdspeff_t eff;
		};
		word_t cid_eff;
	};
	struct _cacccsynnode *next;
} _cacccsynnode_t;

typedef struct _cacccsyn
{
	_cacccsynnode_t * tail;
} _cacccsyn_t;

DECL_COMBINE(cacccsyn)

/* 实现接口 */

SAI void __cacccsyn_init(_cacccsyn_t *s, _cacccsynnode_t *sentry)
{
	s->tail = sentry;
	sentry->next = NULL;
	sentry->flag = COMBINE_FLG_OWNED;
}

SAI void *__cacccsyn_destruct(_cacccsyn_t *s, _cacccsynnode_t **psentry)
{
	ASSERT(s->tail != NULL);
	*psentry = s->tail;
	return (void *)s;
}

SAI _cacccsynnode_t *__cacccsyn_fetch(_cacccsyn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _cacccsynnode_t *node){
	PARAM_USED(s);
	apds_movargs(&node->args, args);
	node->func = pfunc;
	/* 确保已经清空 */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;
	return node;
}

SAI apdsret_t __cacccsyn_wait(_cacccsyn_t *s, _cacccsynnode_t **pnode, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	lkword_t flagwait = COMBINE_FLG_WAIT;
	_cacccsynnode_t *next_node, *cur_node, *tmp_node, *tmp_node_next;
	lkword_t flag;
	apdsret_t ret;
	cid_t cur_cid = tinfo->cid;
	/* hword_t help_quota = APDS_GET_COREQUOTA(cur_cid) - 1; */
	hword_t help_quota = APDS_HELP_QUOTA - 1;
	/* ASSERT(help_quota > 0); */
	apdspeff_t cur_eff = APDS_GET_COREEFF(cur_cid);
	word_t cid_eff = new_word((hword_t)cur_eff, cur_cid);

	next_node = *pnode;
	cur_node = swp_rel(&s->tail, next_node);
	atm_st(&cur_node->cid_eff, cid_eff);
	atm_strel(&cur_node->next, next_node);
	/* mp_rmb(); */
	flag = atm_ldacq(&cur_node->flag);
	while (flag == COMBINE_FLG_WAIT)
	{
		cnt = waitpolicy->waitf(&cur_node->flag, &flagwait, cnt);
		flag = atm_ldacq(&cur_node->flag);
	}

	if (flag == COMBINE_FLG_COMPLETED)
	{
		/* mp_rmb(); */ /* 同一cache行且存在读依赖 */
		*pnode = cur_node;
		return apds_args_ret(&cur_node->args);
	}

	/* mp_rmb(); */

	/* tmp_node = cur_node; */
	tmp_node_next = next_node;
	
	/* 先完成自己 */
	/* apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node_next->func, &tmp_node_next->args)); */
	ret = apds_sync_single(tmp_node_next->func, &tmp_node_next->args);
	/* atm_st(&tmp_node->flag, COMBINE_FLG_COMPLETED); 都不用写入 */
	
	/* 到下一个 */
	tmp_node = tmp_node_next;
	tmp_node_next = tmp_node_next->next;
	
	while (tmp_node_next)
	{
		// 帮助

		/* APDS_PREFETCH(tmp_node_next); */
		apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node_next->func, &tmp_node_next->args));
		atm_strel(&tmp_node->flag, COMBINE_FLG_COMPLETED);

		tmp_node = tmp_node_next;
		
		cid_eff = atm_ld(&tmp_node->cid_eff);
		help_quota = cac_next_helpquota(COMBINE_TEFF(cid_eff), COMBINE_TCID(cid_eff), &cur_eff, cur_cid, help_quota);
		/* quota补偿防止额度消耗完时传递给一个低效率的线程，造成大->小->大的浪费 */
		if ((help_quota == 0) && (cur_eff > COMBINE_TEFF(cid_eff))) { 
			cur_eff -= 1; help_quota += 1; 
		}
		if (help_quota == 0) break;
		
		tmp_node_next = tmp_node_next->next;
	}

	/* 释放所有权 */
	atm_strel(&tmp_node->flag, COMBINE_FLG_OWNED);
	/* 唤醒线程 */
	lkword_t flag_owned = COMBINE_FLG_OWNED;
	waitpolicy->wakef(&tmp_node->flag, &flag_owned);
	*pnode = cur_node;
	return ret;
}

PARAM_NONULL(1) SAI _cacccsyn_t *_cacccsyn_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	if (size < sizeof(_cacccsyn_t)) return NULL;
	_cacccsynnode_t *node = (_cacccsynnode_t *)nodemgr->get_lknode(sizeof(_cacccsynnode_t));
	_cacccsyn_t *s = (_cacccsyn_t *)ptr;
	__cacccsyn_init(s, node);
	return s;
}
PARAM_NONULL(1) SAI void *_cacccsyn_destruct(_cacccsyn_t *s, INFUN(cmbn_node, nodemgr)){
	_cacccsynnode_t *node;
	void *rptr = __cacccsyn_destruct(s, &node);
	nodemgr->ret_lknode(node, sizeof(_cacccsynnode_t));
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _cacccsynnode_t *_cacccsyn_fetch(_cacccsyn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo);
	_cacccsynnode_t *node = (_cacccsynnode_t *)nodemgr->get_lknode(sizeof(_cacccsynnode_t));
	return __cacccsyn_fetch(s, pfunc, args, node);
}
PARAM_NONULL(1, 2, 3) SAI apdsret_t _cacccsyn_wait(_cacccsyn_t *s, _cacccsynnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	apdsret_t ret = __cacccsyn_wait(s, &node, tinfo, waitpolicy);
	nodemgr->ret_lknode(node, sizeof(_cacccsynnode_t));
	return ret;
}

